package com.ycy.chuwenuniapp.service;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ycy.chuwenuniapp.common.ResCodeEnum;
import com.ycy.chuwenuniapp.common.ResData;
import com.ycy.chuwenuniapp.entity.Goods;
import com.ycy.chuwenuniapp.entity.MyFile;
import com.ycy.chuwenuniapp.entity.User;
import com.ycy.chuwenuniapp.exception.CustomException;
import com.ycy.chuwenuniapp.mapper.IMyFileMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.List;

@Service
public class MyFileService extends ServiceImpl<IMyFileMapper, MyFile> {
    @Resource
    private UserService userService;
    @Resource
    private GoodsService goodsService;

    @Value("${spring.servlet.multipart.location}/uploadFiles/")
    private String uploadPath;
    @Value("${spring.servlet.multipart.location}/recycleFiles/")
    private String recycleBinPath;
     // @Value("${cloud-server.url}:${server.port}")
    @Value("${cloud-server.url}")
    private String serveUrl;

    //上传
    public ResData<MyFile> upload(MultipartFile file) {
        if (file == null) throw new CustomException(ResCodeEnum.BAD_REQUEST, "文件不能为空");
        //获得 uuid  IdUtil.fastSimpleUUID()

        try {
            //获取文件的md5,并通过md5查询是否存在
            String md5;
            InputStream inputStream = file.getInputStream();
            md5 = SecureUtil.md5(inputStream);
            inputStream.close();  //inputStream是一个 .tml文件，如果不关闭就会保留这个文件
            QueryWrapper<MyFile> wrapper = new QueryWrapper<>();
            wrapper.eq("md5",md5);
            List<MyFile> sameMd5List = list(wrapper);

            String url;
            String fileName = file.getOriginalFilename();
            String fileType = FileUtil.extName(fileName);
            long fileSize = file.getSize() / 1024;  //单位kb

            if(sameMd5List.isEmpty()) {
                // System.out.println(uploadPath + fileName);
                String uuid = IdUtil.fastSimpleUUID();
                String uniqueName = uuid + "@@" + fileName;
                File saveFile = new File(uploadPath + uniqueName);
                //判断文件夹是否存在，若不存在则创建
                File parentFile = saveFile.getParentFile();
                if(!parentFile.exists()) {
                    boolean isMkdirs = parentFile.mkdirs();
                    if(!isMkdirs) throw new CustomException(ResCodeEnum.BAD_REQUEST, "保存文件时,创建父级目录失败");
                }
                //存储到磁盘
                file.transferTo(saveFile);
                url = serveUrl +  "/file/download?uniqueName=" + uniqueName;

                //存储数据库
                MyFile myFile = new MyFile();
                myFile.setName(uniqueName);
                myFile.setType(fileType);
                myFile.setSize(fileSize);
                myFile.setUrl(url);
                myFile.setMd5(md5);
                myFile.setUseNum(1);
                boolean isSave = save(myFile);
                if(isSave) return ResData.success(myFile);
                else return ResData.success(new MyFile());
            }else {
                MyFile myFile = sameMd5List.get(0);
                myFile.setUseNum(myFile.getUseNum() + 1);
                boolean isUpdate = updateById(myFile);
                if(!isUpdate) throw new CustomException(ResCodeEnum.SERVER_ERROR,"上传文件后,更新文件使用数失败");
                return ResData.success(myFile);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException(ResCodeEnum.SERVER_ERROR,"系统存储文件失败");
        }
    }

    //文件引用次数减一,查原文件在减1后useNum是否为空,如果为空就移入回收站
    public ResData<Boolean> delete(String url) {
        if(StrUtil.isBlank(url)) throw new CustomException(ResCodeEnum.BAD_REQUEST,"文件下载地址不能为空");
        QueryWrapper<MyFile> wrapper = new QueryWrapper<>();
        wrapper.eq("url", url);
        wrapper.select("use_num", "id");
        List<MyFile> fileList = list(wrapper);
        if (fileList.size() > 1) throw new CustomException(ResCodeEnum.BAD_REQUEST, "错误地存在多个该下载地址的信息");
        if (fileList.size() == 0) throw new CustomException(ResCodeEnum.BAD_REQUEST, "该下载地址的信息不存在");
        MyFile myFile = fileList.get(0);
        int nowUseNum = myFile.getUseNum() - 1; //减去自己的使用数
        if (nowUseNum == 0) {
            Boolean isDelete = deleteToRecycleBin(myFile.getId());
            if (!isDelete) throw new CustomException(ResCodeEnum.SERVER_ERROR, "将文件移入回收站失败");
        } else {
            myFile.setUseNum(nowUseNum);
            boolean isUpdate = updateById(myFile);
            if (!isUpdate) throw new CustomException(ResCodeEnum.SERVER_ERROR, "更新原文件使用数失败");
        }
        return ResData.success(true);
    }

    //下载
    public void download(String uniqueName, HttpServletResponse response) {
        try {
            File saveFile = new File(uploadPath + uniqueName);
            String[] splitFileName = uniqueName.split("\\.");
            int pointChartIndex = uniqueName.indexOf(".");
            int spiltChartIndex = uniqueName.indexOf("@@");
            if(pointChartIndex == -1 || spiltChartIndex == -1) throw new CustomException(ResCodeEnum.BAD_REQUEST,"该文件名格式不正确");
            String originName = splitFileName[0].substring(spiltChartIndex + 2,pointChartIndex);
            String fileType = splitFileName[1];

            ServletOutputStream outputStream = response.getOutputStream();
            response.setContentType("application/octet-stream;charset=utf-8");
            String file = URLEncoder.encode(originName,"UTF-8");  //设置文件名称,并进行编码
            response.setHeader("Content-Disposition","attachment;filename=" + file + "." + fileType);  //将文件名设置到 response的 header中

            outputStream.write(FileUtil.readBytes(saveFile));
            outputStream.flush();
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException(ResCodeEnum.BAD_REQUEST,e.getMessage());
        }
    }

    //更新头像
    public ResData<MyFile> updateAvatar(MultipartFile file, String userId) {
        String oldAvatarUrl;
        // String oldAvatarUrl = myFileDTO.getOldAvatarUrl();
        if (file == null) throw new CustomException(ResCodeEnum.BAD_REQUEST, "文件不能为空");
        if(StrUtil.isBlank(userId)) throw new CustomException(ResCodeEnum.BAD_REQUEST,"用户id不能为空");
        // if(StrUtil.isBlank(oldAvatarUrl)) throw new CustomException(ResCodeEnum.BAD_REQUEST,"原头像地址不能为空");

        //获得文件url
        MyFile myFileData = upload(file).getData();
        String newAvatarUrl = myFileData.getUrl();

        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("id",userId);
        List<User> list = userService.list(wrapper);
        if(list.size() > 1) throw new CustomException(ResCodeEnum.BAD_REQUEST,"错误地存在多个该用户id的信息");
        if(list.size() == 0) throw new CustomException(ResCodeEnum.BAD_REQUEST,"该用户id的信息不存在");

        User user = list.get(0);
        oldAvatarUrl = user.getAvatarUrl();
        if(StrUtil.isNotBlank(oldAvatarUrl) && oldAvatarUrl.equals(newAvatarUrl)) return updateSameAvatar(myFileData);  //当更换的是相同头像时
        user.setAvatarUrl(newAvatarUrl);
        boolean isUpdate = userService.updateById(user);
        if(!isUpdate) throw new CustomException(ResCodeEnum.SERVER_ERROR,"头像文件上传成功,但更新用户头像信息失败");

        //查原文件在减1后useNum是否为空。
        if(StrUtil.isNotBlank(oldAvatarUrl)) delete(oldAvatarUrl);

        return ResData.success(myFileData);
    }

    //更换商品图片
//    public ResData<MyFile> updateGoodsImg(MultipartFile file, String goodsId) {
//        String oldUrl;
//        if (file == null) throw new CustomException(ResCodeEnum.BAD_REQUEST, "文件不能为空");
//        if(StrUtil.isBlank(goodsId)) throw new CustomException(ResCodeEnum.BAD_REQUEST,"商品id不能为空");
//
//        //获得文件url
//        MyFile myFileData = upload(file);
//        String newUrl = myFileData.getUrl();
//
//        QueryWrapper<Goods> wrapper = new QueryWrapper<>();
//        wrapper.eq("id",goodsId);
//        List<Goods> list = goodsService.list(wrapper);
//        if(list.size() > 1) throw new CustomException(ResCodeEnum.BAD_REQUEST,"错误地存在多个该商品id的信息");
//        if(list.size() == 0) throw new CustomException(ResCodeEnum.BAD_REQUEST,"该商品id的信息不存在");
//
//        Goods goods = list.get(0);
//        oldUrl = goods.getUrl();
//        if(StrUtil.isNotBlank(oldUrl) && oldUrl.equals(newUrl)) return updateSameAvatar(myFileData);  //当更换的是相同头像时
//        goods.setUrl(newUrl);
//        boolean isUpdate = goodsService.updateById(goods);
//        if(!isUpdate) throw new CustomException(ResCodeEnum.SERVER_ERROR,"商品图片文件上传成功,但更新用户头像信息失败");
//
//        //查原文件在减1后useNum是否为空。
//        checkIsDeleteToRecycleBin(oldUrl);
//
//        return ResData.success(myFileData);
//    }

    //删除至回收站,(将文件本身移动到另一个recycleFiles中。数据库的记录的直接删除)
    private Boolean deleteToRecycleBin(String fileId) {
        if(StrUtil.isBlank(fileId)) throw new CustomException(ResCodeEnum.BAD_REQUEST,"文件id不能为空");

        QueryWrapper<MyFile> wrapper = new QueryWrapper<>();
        wrapper.eq("id",fileId);
        List<MyFile> fileList = list(wrapper);
        if(fileList.size() == 0) throw new CustomException(ResCodeEnum.BAD_REQUEST,"该id的文件不存在");
        if(fileList.size() > 1) throw new CustomException(ResCodeEnum.BAD_REQUEST,"错误地存在多个该id的文件");
        MyFile myFile = fileList.get(0);

        //判断文件夹是否存在，若不存在则创建
        File recycleFile = new File(recycleBinPath + myFile.getName());
        File parentFile = recycleFile.getParentFile();
        if(!parentFile.exists()) {
            boolean isMkdirs = parentFile.mkdirs();
            if(!isMkdirs) throw new CustomException(ResCodeEnum.BAD_REQUEST, "删除文件至回收站时,创建父级目录失败");
        }

        //文件删除 Files.deleteIfExists(Paths.get(文件地址));
        //文件复制 Files.copy(Paths.get(源文件地址),Paths.get(新文件地址)) 或 Files.copy(Paths.get(源文件地址),Paths.get(新文件地址), StandardCopyOption.REPLACE_EXISTING);
        //以下是文件移动(若重命名，就复制到相同文件夹，只将文件名称变一下)
        //copy和move方法有第三个参数：StandardCopyOption,
        //StandardCopyOption包含三个选项：REPLACE_EXISTING(当文件存在时，直接覆盖),ATOMIC_MOVE(原子化移动,不会产生先复制再删除失败的情况),COPY_ATTRIBUTES(当文件存在时，复制其中的文件)
        Path originPath = Paths.get(uploadPath + myFile.getName());
        Path targetPath = Paths.get(recycleBinPath + myFile.getName());
        try {
            Files.move(originPath,targetPath,StandardCopyOption.ATOMIC_MOVE);
        } catch (IOException e) {
            e.printStackTrace();
            throw new CustomException(ResCodeEnum.BAD_REQUEST,"文件移动至回收站失败");
        }
        return removeById(fileId);
    }

    private ResData<MyFile> updateSameAvatar(MyFile myFileData) {
        MyFile myFile = new MyFile();
        myFile.setId(myFileData.getId());
        myFile.setUseNum(myFileData.getUseNum() - 1);
        boolean isUpdate = updateById(myFile);
        if(!isUpdate) throw new CustomException(ResCodeEnum.SERVER_ERROR,"替换相同图片时,更新文件使用数失败");
        return ResData.success(myFileData);
    }
}
